package com.ferryc.commons.scan;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.method.HandlerMethod;

import com.ferryc.commons.config.Global;
import com.ferryc.commons.exception.BaseException;
import com.ferryc.commons.utils.CacheUtils;
import com.ferryc.commons.utils.CommonUtil;
import com.ferryc.commons.utils.Exceptions;
import com.ferryc.commons.utils.StringUtil;
import com.ferryc.constant.GlobalConstant;
import com.ferryc.modules.sys.entity.SysLogInfo;
import com.ferryc.modules.sys.entity.SysMenu;
import com.ferryc.modules.sys.entity.SysUser;
import com.ferryc.modules.sys.mapper.ISysLogInfoMapper;
import com.ferryc.modules.sys.mapper.ISysMenuMapper;
import com.ferryc.modules.sys.util.UserUtils;

public class LogUtils {

	public static final String CACHE_MENU_NAME_PATH_MAP = "menuNamePathMap";

	private ISysLogInfoMapper sysLogInfoMapper = SpringContextHolder.getBean(ISysLogInfoMapper.class);
	private ISysMenuMapper sysMenuMapper = SpringContextHolder.getBean(ISysMenuMapper.class);

	/**
	 * 保存日志
	 */
	public static void saveLog(HttpServletRequest request, String title) {
		saveLog(request, null, null, title, null);
	}

	/**
	 * 保存日志
	 */
	public static void saveLog(HttpServletRequest request, Object handler, Exception ex, String title, String context) {
		SysLogInfo log = new SysLogInfo();
		log.setMenu(title);
		log.setType(ex == null ? SysLogInfo.InfoType.ACCESS.getValue() : SysLogInfo.InfoType.EXCEPTION.getValue());
		log.setIpAddr(CommonUtil.getIpAddr(request));
		log.setUserAgent(request.getHeader("user-agent"));
		log.setRequestUri(request.getRequestURI());
		log.setParams(StringUtil.paramToStr(request.getParameterMap()));
		log.setMethod(request.getMethod());
		log.setException(context);
		// 异步保存日志
		new LogUtils().new SaveLogThread(log, handler, ex).start();
	}

	/**
	 * 保存日志线程
	 */
	public class SaveLogThread extends Thread {

		private SysLogInfo log;
		private Object handler;
		private Exception ex;

		public SaveLogThread(SysLogInfo log, Object handler, Exception ex) {
			super(SaveLogThread.class.getSimpleName());
			this.log = log;
			this.handler = handler;
			this.ex = ex;
		}

		@Override
		public void run() {
			// 获取日志标题
			if (StringUtil.isBlank(log.getMenu())) {
				String permission = "";
				if (handler instanceof HandlerMethod) {
					Method m = ((HandlerMethod) handler).getMethod();
					RequiresPermissions rp = m.getAnnotation(RequiresPermissions.class);
					permission = rp != null ? StringUtil.join(rp.value(), ",") : "";
				}
				log.setMenu(getMenuNamePath(log.getRequestUri(), permission));
			}
			// 如果有异常，设置异常信息
			if (null != ex) {
				if (ex instanceof BaseException) {
					log.setException(ex.getMessage());
				} else {
					log.setException(Exceptions.getStackTraceAsString(ex));
				}
			}
			// 如果无标题并无异常日志，则不保存信息
			if (StringUtil.isBlank(log.getMenu()) && StringUtil.isBlank(log.getException())) {
				return;
			}
			// 保存日志信息
			SysUser user = UserUtils.getShiroUser();
			if (null != user && StringUtil.isNotBlank(user.getId())) {
				log.setCreateBy(user);
			}
			log.setCreateDate(new Date());
			log.setId(StringUtil.getUUId());
			sysLogInfoMapper.insert(log);
		}
	}

	/**
	 * 获取菜单名称路径（如：系统设置-机构用户-用户管理-编辑）
	 */
	public String getMenuNamePath(String requestUri, String permission) {
		String href = StringUtils.substringAfter(requestUri, Global.getAdminPath());
		@SuppressWarnings("unchecked")
		Map<String, String> menuMap = (Map<String, String>) CacheUtils.get(CACHE_MENU_NAME_PATH_MAP);
		if (menuMap == null) {
			menuMap = createMenuNameList();
			CacheUtils.put(CACHE_MENU_NAME_PATH_MAP, menuMap);
		}
		String menuNamePath = menuMap.get(href);
		if (menuNamePath == null) {
			for (String p : StringUtils.split(permission)) {
				menuNamePath = menuMap.get(p);
				if (StringUtil.isNotBlank(menuNamePath)) {
					break;
				}
			}
			if (menuNamePath == null) {
				return "";
			}
		}
		return menuNamePath;
	}

	/**
	 * 获取菜单名称路径（如：系统设置-机构用户-用户管理-编辑）
	 */
	protected Map<String, String> createMenuNameList() {
		Map<String, String> menuMap = new HashMap();
		SysMenu entity = new SysMenu();
		entity.setDelFlag(GlobalConstant.NO);
		List<SysMenu> menuList = sysMenuMapper.listByEntity(entity);
		for (SysMenu menu : menuList) {
			// 获取菜单名称路径（如：系统设置-机构用户-用户管理-编辑）
			String menuNamePath = parentMenuName(menuList, menu.getParentId());
			List<String> nameList = new ArrayList();
			if (StringUtil.isNotBlank(menuNamePath)) {
				String[] names = menuNamePath.split("-");
				for (int i = names.length - 1; i >= 0; i--) {
					nameList.add(names[i]);
				}
			}
			nameList.add(menu.getMenuName());
			String namePath = StringUtil.join(nameList, "-");
			if (StringUtil.isNotBlank(menu.getPermission())) {
				menuMap.put(menu.getPermission(), namePath);
			}
		}
		return menuMap;
	}

	/**
	 * 获取父级菜单名称
	 * 
	 * @param menuList
	 * @param parentId
	 * @return
	 */
	protected String parentMenuName(List<SysMenu> menuList, String parentId) {
		List<String> nameList = new ArrayList();
		for (Iterator iterator = menuList.iterator(); iterator.hasNext();) {
			SysMenu sysMenu = (SysMenu) iterator.next();
			if (SysMenu.SYSTEM_PARENT_ID.equals(sysMenu.getParentId()) || SysMenu.SYSTEM_ID.equals(parentId)) {
				continue;
			}
			if (parentId.equals(sysMenu.getId())) {
				nameList.add(sysMenu.getMenuName());
				if (!SysMenu.SYSTEM_PARENT_ID.equals(sysMenu.getParentId()) || !SysMenu.SYSTEM_ID.equals(parentId)) {
					nameList.add(parentMenuName(menuList, sysMenu.getParentId()));
				}
			}
		}
		return StringUtil.join(nameList, "-");
	}

}
